#include "pca_test.h"
#include "dag_nodes.h"
#include "dag_operators.h"

#include <memory>
#include <iostream>
#include <iomanip>

using namespace dag;
using namespace std;

namespace {
	auto_ptr<FrameDepot> gen_depot(const FrameSpec& spec) {
		auto_ptr<FrameDepot> depot(new FrameDepot(spec));
		Frame frame(spec);
		for (size_t i = 0; i < 10000; i++) {
			for (size_t j = 0; j < spec.bSize; j++) {
				frame.bools[j] = (rand() > RAND_MAX / 2);
			}
			for (size_t j = 0; j < spec.iSize; j++) {
				frame.ints[j] = rand();
			}
			for (size_t j = 0; j < spec.fSize; j++) {
				if (j == 1) {
					frame.floats[j] = 100.0f * float(rand()) / float(RAND_MAX);
				}
				else if (j == 5) {
					frame.floats[j] = 1000.0f * float(rand()) / float(RAND_MAX);
				}
				else {
					frame.floats[j] = float(rand()) / float(RAND_MAX);
				}
			}
			for (size_t j = 0; j < spec.dSize; j++) {
				if (j == 1) {
					frame.doubles[j] = 100.0 * double(rand()) / double(RAND_MAX);
				}
				else if (j == 5) {
					frame.doubles[j] = 1000.0 * double(rand()) / double(RAND_MAX);
				}
				else {
					frame.doubles[j] = double(rand()) / double(RAND_MAX);
				}
			}
			depot->add(frame);
		}
		
		return depot;
	}
	
	template<typename _type>
	void print_transform(const _type* const* trans, size_t rows, size_t cols) {
		for (size_t i = 0; i < rows; i++) {
			for (size_t j = 0; j < cols; j++) {
				cout << setw(10) << setprecision(3) << trans[i][j];
			}
			cout << endl;
		}
	}
	
	void print_linear_op(const LinearOp& op) {
		cout << "bools" << endl;
		print_transform(op.boolTransform(), 
						op.outputSpec().bSize, 
						op.inputSpec().bSize + 1);
		cout << "ints" << endl;
		print_transform(op.intTransform(), 
						op.outputSpec().iSize, 
						op.inputSpec().iSize + 1);
		cout << "floats" << endl;
		print_transform(op.floatTransform(), 
						op.outputSpec().fSize, 
						op.inputSpec().fSize + 1);
		cout << "doubles" << endl;
		print_transform(op.doubleTransform(), 
						op.outputSpec().dSize, 
						op.inputSpec().dSize + 1);
	}
	
}

PcaTest::PcaTest() {
}

PcaTest::~PcaTest() throw() {
}

const char* PcaTest::description() {
	return "pca_test";
}

bool PcaTest::run() {
	return testInputPca() && testOutputPca();
}

bool PcaTest::testInputPca() {
	FrameSpec input(0, 0, 10, 10);
	FrameSpec output(0, 0, 4, 4);
	
	PcaNode node(output, true);
	node.init(input);
	auto_ptr<FrameDepot> depot = gen_depot(input);
	node.learn(depot.get(), NULL);
	
	auto_ptr<FrameOperator> fop = node.createForwardOp();
	auto_ptr<FrameOperator> rop = node.createReverseOp();
	
	LinearOp& flop = dynamic_cast<LinearOp&> (*fop);
	LinearOp& rlop = dynamic_cast<LinearOp&> (*rop);
	
	cout << "Forward" << endl;
	print_linear_op(flop);
	cout << "Reverse" << endl;
	print_linear_op(rlop);
	
	return true;
}

bool PcaTest::testOutputPca() {
	FrameSpec output(0, 0, 10, 10);
	FrameSpec input(0, 0, 4, 4);
	
	PcaNode node(output, false);
	node.init(input);
	auto_ptr<FrameDepot> depot = gen_depot(output);
	node.learn(NULL, depot.get());
	
	auto_ptr<FrameOperator> fop = node.createForwardOp();
	auto_ptr<FrameOperator> rop = node.createReverseOp();
	
	LinearOp& flop = dynamic_cast<LinearOp&> (*fop);
	LinearOp& rlop = dynamic_cast<LinearOp&> (*rop);
	
	cout << "Forward" << endl;
	print_linear_op(flop);
	cout << "Reverse" << endl;
	print_linear_op(rlop);
	
	return true;
}